#ifndef __COLOR_3_HPP__
#define __COLOR_3_HPP__

#define USE_NLOHMANN_JSON 1
#define USE_FMT_TO_STRING 1

#include <stdint.h>
#include <assert.h>

#if USE_NLOHMANN_JSON
#include "nlohmann/json.hpp"
#endif

#if USE_FMT_TO_STRING
#include "spdlog/fmt/fmt.h"
#endif

namespace PocketSurvival::Engine
{
    struct Color3
    {
        union
        {
            struct
            {
                uint8_t R;
                uint8_t G;
                uint8_t B;
            };
            int32_t value;
        };

        constexpr Color3() noexcept;
        constexpr Color3(uint8_t r, uint8_t g, uint8_t b) noexcept;
        constexpr Color3(const Color3 &left) noexcept;

        constexpr inline Color3 &operator+=(const Color3 &left);
        constexpr inline Color3 &operator-=(const Color3 &left);
        constexpr inline Color3 &operator*=(uint8_t c);
        constexpr inline Color3 &operator/=(uint8_t c);
        constexpr inline Color3 &operator=(const Color3 &left);

        friend constexpr inline bool operator==(const Color3 &left, const Color3 &right)
        {
            return (left.R == right.R) && (left.G == right.G) && (left.B == right.B);
        }
        friend constexpr inline bool operator!=(const Color3 &left, const Color3 &right)
        {
            return !(left == right);
        }
        friend constexpr inline Color3 operator+(const Color3 &left, const Color3 &right)
        {
            return Color3(left.R + right.R, left.G + right.G, left.B + right.B);
        }

        friend constexpr inline Color3 operator-(const Color3 &left, const Color3 &right)
        {
            return Color3(left.R - right.R, left.G - right.G, left.B - right.B);
        }
        friend constexpr inline Color3 operator*(const Color3 &left, uint8_t c)
        {
            return Color3(left.R * c, left.G * c, left.B * c);
        }
        friend constexpr inline Color3 operator*(uint8_t c, const Color3 &right)
        {
            return Color3(right.R * c, right.G * c, right.B * c);
        }
        friend constexpr inline Color3 operator+(const Color3 &left)
        {
            return left;
        }
        friend constexpr inline Color3 operator-(const Color3 &left)
        {
            return Color3((uint8_t)-left.R, (uint8_t)-left.G, (uint8_t)-left.B);
        }
        friend constexpr inline Color3 operator/(const Color3 &left, uint8_t divisor)
        {
            assert(divisor < 0 || divisor > 0);
            return Color3(left.R / divisor, left.G / divisor, left.B / divisor);
        }

#if USE_NLOHMANN_JSON
        friend inline void to_json(nlohmann::json &json, const Color3 &color);
        friend inline void from_json(const nlohmann::json &json, Color3 &color);
#endif

#if USE_FMT_TO_STRING
        inline std::string toString()
        {
            return fmt::format("{},{},{}",R,G,B);
        }
#endif
    };

    constexpr inline Color3::Color3() noexcept : R(0), G(0), B(0) {}

    constexpr inline Color3::Color3(uint8_t r, uint8_t g, uint8_t b) noexcept : R(r), G(g), B(b) {}

    constexpr inline Color3::Color3(const Color3 &left) noexcept : R(left.R), G(left.G), B(left.B) {}

    constexpr inline Color3 &Color3::operator+=(const Color3 &right)
    {
        R += right.R;
        G += right.G;
        B += right.B;
        return *this;
    }

    constexpr inline Color3 &Color3::operator-=(const Color3 &right)
    {
        R -= right.R;
        G -= right.G;
        B -= right.B;
        return *this;
    }

    constexpr inline Color3 &Color3::operator*=(uint8_t c)
    {
        R *= c;
        G *= c;
        B *= c;
        return *this;
    }

    constexpr inline Color3 &Color3::operator/=(uint8_t divisor)
    {
        assert(divisor > 0 || divisor < 0);
        R /= divisor;
        G /= divisor;
        B /= divisor;
        return *this;
    }

    constexpr inline Color3 &Color3::operator=(const Color3 &left)
    {
        R = left.R;
        G = left.G;
        B = left.B;
        return *this;
    }

#if USE_NLOHMANN_JSON

    inline void to_json(nlohmann::json &json, const Color3 &color)
    {
        json = color.value;
    }
    inline void from_json(const nlohmann::json &json, Color3 &color)
    {
        color.value = json.get<int32_t>();
    }

#endif

}

#undef USE_FMT_TO_STRING
#undef USE_NLOHMANN_JSON


#endif